#include "../src/BigInteger.hpp"

#include <gmpxx.h>

#include <catch2/catch_message.hpp>
#include <vector>

#define CATCH_CONFIG_MAIN
#include <catch2/benchmark/catch_benchmark.hpp>
#include <catch2/catch_test_macros.hpp>

using WinstJackonw::BigInteger;
// NOLINTBEGIN
// 辅助函数：将mpz_class转换为字符串
std::string mpzToString(const mpz_class& num) { return num.get_str(); }

#define compareBigIntWithGMP(bi, gmp) \
  REQUIRE((bi).to_string() == mpzToString((gmp)));

TEST_CASE("Constructor tests", "[constructor]") {
  SECTION("Default constructor") {
    BigInteger bi;
    mpz_class gmp(0);
    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("From string - positive") {
    BigInteger bi("12345678901234567890");
    mpz_class gmp("12345678901234567890");
    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("From string - negative") {
    BigInteger bi("-12345678901234567890");
    mpz_class gmp("-12345678901234567890");
    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("From string - with leading zeros") {
    BigInteger bi("000001234567890");
    mpz_class gmp("1234567890");
    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("From string - zero") {
    BigInteger bi("0000");
    mpz_class gmp(0);
    compareBigIntWithGMP(bi, gmp);
    REQUIRE_FALSE(bi.is_negative());
  }

  SECTION("From int - positive") {
    BigInteger bi(123456789);
    mpz_class gmp(123456789);
    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("From int - negative") {
    BigInteger bi(-123456789);
    mpz_class gmp(-123456789);
    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("From unsigned int") {
    BigInteger bi(1234567890U);
    mpz_class gmp(1234567890U);
    compareBigIntWithGMP(bi, gmp);
  }
}

TEST_CASE("Comparison tests", "[comparison]") {
  SECTION("Equal") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("12345678901234567890");
    REQUIRE(bi1 == bi2);
  }

  SECTION("Not equal") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("12345678901234567891");
    REQUIRE(bi1 != bi2);
  }

  SECTION("Less than") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("12345678901234567891");
    REQUIRE(bi1 < bi2);
  }

  SECTION("Greater than") {
    BigInteger bi1("12345678901234567891");
    BigInteger bi2("12345678901234567890");
    REQUIRE(bi1 > bi2);
  }

  SECTION("Less than or equal") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("12345678901234567890");
    BigInteger bi3("12345678901234567891");
    REQUIRE(bi1 <= bi2);
    REQUIRE(bi1 <= bi3);
  }

  SECTION("Greater than or equal") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("12345678901234567890");
    BigInteger bi3("12345678901234567889");
    REQUIRE(bi1 >= bi2);
    REQUIRE(bi1 >= bi3);
  }
}

TEST_CASE("Arithmetic operations", "[arithmetic]") {
  SECTION("Addition - positive") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("98765432109876543210");
    mpz_class gmp1("12345678901234567890");
    mpz_class gmp2("98765432109876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
  }

  SECTION("Addition - negative") {
    BigInteger bi1("-12345678901234567890");
    BigInteger bi2("-98765432109876543210");
    mpz_class gmp1("-12345678901234567890");
    mpz_class gmp2("-98765432109876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
  }

  SECTION("Addition - mixed signs") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("-98765432109876543210");
    mpz_class gmp1("12345678901234567890");
    mpz_class gmp2("-98765432109876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
  }

  SECTION("Subtraction - positive") {
    BigInteger bi1("98765432109876543210");
    BigInteger bi2("12345678901234567890");
    mpz_class gmp1("98765432109876543210");
    mpz_class gmp2("12345678901234567890");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 - bi2, gmp1 - gmp2);
  }

  SECTION("Subtraction - negative") {
    BigInteger bi1("-98765432109876543210");
    BigInteger bi2("-12345678901234567890");
    mpz_class gmp1("-98765432109876543210");
    mpz_class gmp2("-12345678901234567890");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 - bi2, gmp1 - gmp2);
  }

  SECTION("Subtraction - mixed signs") {
    BigInteger bi1("98765432109876543210");
    BigInteger bi2("-12345678901234567890");
    mpz_class gmp1("98765432109876543210");
    mpz_class gmp2("-12345678901234567890");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 - bi2, gmp1 - gmp2);
  }

  SECTION("Multiplication - positive") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("9876543210");
    mpz_class gmp1("12345678901234567890");
    mpz_class gmp2("9876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 * bi2, gmp1 * gmp2);
  }

  SECTION("Multiplication - negative") {
    BigInteger bi1("-12345678901234567890");
    BigInteger bi2("-9876543210");
    mpz_class gmp1("-12345678901234567890");
    mpz_class gmp2("-9876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 * bi2, gmp1 * gmp2);
  }

  SECTION("Multiplication - mixed signs") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("-9876543210");
    mpz_class gmp1("12345678901234567890");
    mpz_class gmp2("-9876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 * bi2, gmp1 * gmp2);
  }

  SECTION("Division - positive") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("9876543210");
    mpz_class gmp1("12345678901234567890");
    mpz_class gmp2("9876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 / bi2, gmp1 / gmp2);
  }

  SECTION("Division - negative") {
    BigInteger bi1("-12345678901234567890");
    BigInteger bi2("-9876543210");
    mpz_class gmp1("-12345678901234567890");
    mpz_class gmp2("-9876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 / bi2, gmp1 / gmp2);
  }

  SECTION("Division - mixed signs") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2("-9876543210");
    mpz_class gmp1("12345678901234567890");
    mpz_class gmp2("-9876543210");
    CAPTURE(bi1, bi2, gmp1, gmp2);
    compareBigIntWithGMP(bi1 / bi2, gmp1 / gmp2);
  }

  SECTION("Division by zero") {
    BigInteger bi1("12345678901234567890");
    BigInteger bi2(0);
    CAPTURE(bi1, bi2);
    REQUIRE_THROWS_AS(bi1 / bi2, std::runtime_error);
  }
}

TEST_CASE("Unary operations", "[unary]") {
  SECTION("Negation - positive") {
    BigInteger bi("12345678901234567890");
    mpz_class gmp("12345678901234567890");

    compareBigIntWithGMP(-bi, -gmp);
  }

  SECTION("Negation - negative") {
    BigInteger bi("-12345678901234567890");
    mpz_class gmp("-12345678901234567890");

    compareBigIntWithGMP(-bi, -gmp);
  }

  SECTION("Negation - zero") {
    BigInteger bi(0);
    mpz_class gmp(0);

    compareBigIntWithGMP(-bi, -gmp);
    REQUIRE_FALSE((-bi).is_negative());
  }
}

TEST_CASE("Edge cases", "[edge]") {
  SECTION("Very large numbers") {
    std::string largeStr(1000, '9');
    BigInteger bi(largeStr);
    mpz_class gmp(largeStr);

    compareBigIntWithGMP(bi, gmp);
  }

  SECTION("Addition with carry") {
    BigInteger bi1("99999999999999999999");
    BigInteger bi2(1);
    mpz_class gmp1("99999999999999999999");
    mpz_class gmp2(1);

    compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
  }

  SECTION("Subtraction with borrow") {
    BigInteger bi1("10000000000000000000");
    BigInteger bi2(1);
    mpz_class gmp1("10000000000000000000");
    mpz_class gmp2(1);

    compareBigIntWithGMP(bi1 - bi2, gmp1 - gmp2);
  }
}

TEST_CASE("Formatted output", "[format]") {
  SECTION("Default format") {
    BigInteger bi("12345678901234567890");
    std::string result = std::format("{}", bi);
    REQUIRE(result == "12345678901234567890");
  }

  SECTION("Negative format") {
    BigInteger bi("-12345678901234567890");
    std::string result = std::format("{}", bi);
    REQUIRE(result == "-12345678901234567890");
  }

  SECTION("Zero format") {
    BigInteger bi(0);
    std::string result = std::format("{}", bi);
    REQUIRE(result == "0");
  }
}

#include <random>

std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());

template <typename U = std::int32_t, class Container = std::vector<U>>
std::pair<BigInteger<U, Container>, mpz_class> generateRandomBigInt(
    int max_digits = 100) {
  std::uniform_int_distribution<int> digit_dist(0, 9);
  std::uniform_int_distribution<int> sign_dist(0, 1);

  // 生成随机位数（至少1位）
  int num_digits = std::uniform_int_distribution<int>(1, max_digits)(rng);

  // 生成随机符号
  bool is_negative = sign_dist(rng) == 1;

  // 生成第一位（不能是0）
  std::string num_str;
  num_str += std::to_string(std::uniform_int_distribution<int>(1, 9)(rng));

  // 生成剩余位数
  for (int i = 1; i < num_digits; ++i) {
    num_str += std::to_string(digit_dist(rng));
  }

  // 添加符号
  if (is_negative) {
    num_str = "-" + num_str;
  }

  return {WinstJackonw::BigInteger<U, Container>(num_str), mpz_class(num_str)};
}

TEST_CASE("Random constructor tests", "[random][constructor]") {
  const int NUM_TESTS = 100;

  for (int i = 0; i < NUM_TESTS; ++i) {
    auto [bi, gmp] = generateRandomBigInt<std::int32_t>();
    compareBigIntWithGMP(bi, gmp);
  }
  for (int i = 0; i < NUM_TESTS; ++i) {
    auto [bi, gmp] = generateRandomBigInt<std::int32_t>();
    compareBigIntWithGMP(bi, gmp);
  }
}

TEST_CASE("Random comparison tests", "[random][comparison]") {
  const int NUM_TESTS = 50;

  for (int i = 0; i < NUM_TESTS; ++i) {
    auto [bi1, gmp1] = generateRandomBigInt<std::int32_t>();
    auto [bi2, gmp2] = generateRandomBigInt<std::int32_t>();
    CAPTURE(bi1, bi2, gmp1, gmp2);
    REQUIRE((bi1 == bi2) == (gmp1 == gmp2));
    REQUIRE((bi1 != bi2) == (gmp1 != gmp2));
    REQUIRE((bi1 < bi2) == (gmp1 < gmp2));
    REQUIRE((bi1 > bi2) == (gmp1 > gmp2));
    REQUIRE((bi1 <= bi2) == (gmp1 <= gmp2));
    REQUIRE((bi1 >= bi2) == (gmp1 >= gmp2));
  }
}

TEST_CASE("Random arithmetic operations", "[random][arithmetic]") {
  const int NUM_TESTS = 100;

  SECTION("Addition") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();
      auto [bi2, gmp2] = generateRandomBigInt();

      compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
    }
  }

  SECTION("Subtraction") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();
      auto [bi2, gmp2] = generateRandomBigInt();

      compareBigIntWithGMP(bi1 - bi2, gmp1 - gmp2);
    }
  }

  SECTION("Multiplication") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();
      auto [bi2, gmp2] = generateRandomBigInt();

      compareBigIntWithGMP(bi1 * bi2, gmp1 * gmp2);
    }
  }

  SECTION("Division") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();

      // 确保除数不为零
      mpz_class gmp2;
      do {
        auto [bi2, gmp2_temp] = generateRandomBigInt();
        gmp2 = gmp2_temp;
      } while (gmp2 == 0);

      BigInteger bi2(std::string(gmp2.get_str()));
      CAPTURE(bi1, bi2, gmp1, gmp2);
      compareBigIntWithGMP(bi1 / bi2, gmp1 / gmp2);
    }
  }
}

TEST_CASE("Random unary operations", "[random][unary]") {
  const int NUM_TESTS = 50;

  for (int i = 0; i < NUM_TESTS; ++i) {
    auto [bi, gmp] = generateRandomBigInt();

    compareBigIntWithGMP(-bi, -gmp);
  }
}

TEST_CASE("Random edge cases", "[random][edge]") {
  const int NUM_TESTS = 20;

  SECTION("Addition with carry") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      // 生成全为9的数字
      int num_digits = std::uniform_int_distribution<int>(1, 50)(rng);
      std::string nines(num_digits, '9');

      BigInteger bi1(nines);
      BigInteger bi2(1);

      mpz_class gmp1(nines);
      mpz_class gmp2(1);

      compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
    }
  }

  SECTION("Subtraction with borrow") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      // 生成10的幂次方
      int exponent = std::uniform_int_distribution<int>(1, 50)(rng);
      std::string one_followed_by_zeros = "1" + std::string(exponent, '0');

      BigInteger bi1(one_followed_by_zeros);
      BigInteger bi2(1);

      mpz_class gmp1(one_followed_by_zeros);
      mpz_class gmp2(1);

      compareBigIntWithGMP(bi1 - bi2, gmp1 - gmp2);
    }
  }

  SECTION("Multiplication by zero") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      compareBigIntWithGMP(bi * BigInteger(0), gmp * 0);
    }
  }

  SECTION("Multiplication by one") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      compareBigIntWithGMP(bi * BigInteger(1), gmp * 1);
      compareBigIntWithGMP(bi * BigInteger(-1), gmp * -1);
    }
  }

  SECTION("Division by one") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      compareBigIntWithGMP(bi / BigInteger(1), gmp / 1);
      compareBigIntWithGMP(bi / BigInteger(-1), gmp / -1);
    }
  }
}

TEST_CASE("Special random cases", "[random][special]") {
  const int NUM_TESTS = 20;

  SECTION("Zero values") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      // 测试加减零
      compareBigIntWithGMP(bi + BigInteger(0), gmp + 0);
      compareBigIntWithGMP(bi - BigInteger(0), gmp - 0);

      // 测试零加减
      compareBigIntWithGMP(BigInteger(0) + bi, 0 + gmp);
      compareBigIntWithGMP(BigInteger(0) - bi, 0 - gmp);
    }
  }

  SECTION("Self operations") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      // 测试自减自加
      compareBigIntWithGMP(bi - bi, gmp - gmp);
      compareBigIntWithGMP(bi + (-bi), gmp + (-gmp));
    }
  }

  SECTION("Absolute value") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      compareBigIntWithGMP(bi.get_abs(), abs(gmp));
    }
  }
}

TEST_CASE("Property-based tests", "[random][property]") {
  const int NUM_TESTS = 50;

  SECTION("Commutative property of addition") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();
      auto [bi2, gmp2] = generateRandomBigInt();

      REQUIRE((bi1 + bi2) == (bi2 + bi1));
      compareBigIntWithGMP(bi1 + bi2, gmp1 + gmp2);
    }
  }

  SECTION("Associative property of addition") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();
      auto [bi2, gmp2] = generateRandomBigInt();
      auto [bi3, gmp3] = generateRandomBigInt();

      BigInteger left = (bi1 + bi2) + bi3;
      BigInteger right = bi1 + (bi2 + bi3);

      REQUIRE(left == right);
      compareBigIntWithGMP(left, (gmp1 + gmp2) + gmp3);
    }
  }

  SECTION("Distributive property") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi1, gmp1] = generateRandomBigInt();
      auto [bi2, gmp2] = generateRandomBigInt();
      auto [bi3, gmp3] = generateRandomBigInt();

      BigInteger left = bi1 * (bi2 + bi3);
      BigInteger right = (bi1 * bi2) + (bi1 * bi3);

      REQUIRE(left == right);
      compareBigIntWithGMP(left, gmp1 * (gmp2 + gmp3));
    }
  }

  SECTION("Double negation") {
    for (int i = 0; i < NUM_TESTS; ++i) {
      auto [bi, gmp] = generateRandomBigInt();

      REQUIRE(-(-bi) == bi);
      compareBigIntWithGMP(-(-bi), -(-gmp));
    }
  }
}

// NOLINTEND